package com.ebo.httpcall4j.spring;

import com.ebo.httpcall4j.core.inbound.LogIncomingCall;
import com.ebo.httpcall4j.core.model.HttpCallRequest;
import com.ebo.httpcall4j.core.model.enums.HttpMethod;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

/**
 * 切面，用于在被@LogIncomingCall注解的方法执行前，准备HTTP调用请求的数据。
 * 它构建HttpCallRequest对象，并将其存储在HttpServletRequest属性中，供HttpCallLogInterceptor后续使用。
 */
@Aspect
public class LogIncomingCallAspect {

    public LogIncomingCallAspect() {
    }

    /**
     * 环绕通知，在注解了@LogIncomingCall的方法周围执行。
     *
     * @param joinPoint 连接点，代表被拦截的方法。
     * @return 方法执行的结果。
     * @throws Throwable 如果被拦截的方法抛出异常。
     */
    @Around("@annotation(com.ebo.httpcall4j.core.inbound.LogIncomingCall)")
    public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes == null) {
            return joinPoint.proceed();
        }
        HttpServletRequest httpRequest = attributes.getRequest();

        HttpCallRequest callRequest = buildHttpCallRequest(httpRequest, joinPoint);
        callRequest.setIp(getClientIp(httpRequest));
        httpRequest.setAttribute("httpCallRequest", callRequest);

        return joinPoint.proceed();
    }

    /**
     * 获取客户端IP地址。
     *
     * @param request HttpServletRequest对象。
     * @return 客户端IP地址。
     */
    private String getClientIp(HttpServletRequest request) {
        String xfHeader = request.getHeader("X-Forwarded-For");
        if (xfHeader == null || xfHeader.isEmpty() || "unknown".equalsIgnoreCase(xfHeader)) {
            xfHeader = request.getHeader("Proxy-Client-IP");
        }
        if (xfHeader == null || xfHeader.isEmpty() || "unknown".equalsIgnoreCase(xfHeader)) {
            xfHeader = request.getHeader("WL-Proxy-Client-IP");
        }
        if (xfHeader == null || xfHeader.isEmpty() || "unknown".equalsIgnoreCase(xfHeader)) {
            xfHeader = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (xfHeader == null || xfHeader.isEmpty() || "unknown".equalsIgnoreCase(xfHeader)) {
            xfHeader = request.getRemoteAddr();
        }
        if (xfHeader != null && !xfHeader.isEmpty() && !"unknown".equalsIgnoreCase(xfHeader)) {
            return xfHeader.split(",")[0];
        }
        return request.getRemoteAddr();
    }

    /**
     * 构建HttpCallRequest对象。
     *
     * @param httpRequest HttpServletRequest对象。
     * @param joinPoint   连接点，代表被拦截的方法。
     * @return 构建好的HttpCallRequest对象。
     */
    private HttpCallRequest buildHttpCallRequest(HttpServletRequest httpRequest, ProceedingJoinPoint joinPoint) {
        HttpCallRequest callRequest = new HttpCallRequest();
        callRequest.setUrl(httpRequest.getRequestURL().toString());
        callRequest.setMethod(HttpMethod.valueOf(httpRequest.getMethod().toUpperCase()));
        callRequest.setHeaders(extractHeaders(httpRequest));
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        LogIncomingCall annotation = method.getAnnotation(LogIncomingCall.class);

        String systemName = annotation.systemName();
        String systemNameHeader = annotation.systemNameHeader();

        if (systemNameHeader != null && !systemNameHeader.isEmpty()) {
            String headerValue = httpRequest.getHeader(systemNameHeader);
            if (headerValue != null && !headerValue.isEmpty()) {
                systemName = headerValue;
            }
        }
        callRequest.setSystemName(systemName);

        String functionName = annotation.functionName().isEmpty() ? method.getName() : annotation.functionName();
        callRequest.setFunctionName(functionName);

        return callRequest;
    }

    /**
     * 从请求中提取所有头部信息。
     *
     * @param request HttpServletRequest对象。
     * @return 包含所有头部信息的Map。
     */
    private Map<String, String> extractHeaders(HttpServletRequest request) {
        Map<String, String> headers = new HashMap<>();
        Enumeration<String> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String key = headerNames.nextElement();
            headers.put(key, request.getHeader(key));
        }
        return headers;
    }
}